// sshterm.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include <timing.h>
#include "sshterm.h"
#include "terminal.h"

HMODULE hTiming=NULL;

SCHEDULE_TIMER_PROC schedule_timer=NULL;
RUN_TIMERS_PROC run_timers=NULL;
EXPIRE_TIMER_CONTEXT_PROC expire_timer_context=NULL;
GET_NEXT_TIME_PROC get_next_time=NULL;
TIMER_CHANGE_NOTIFY_PROC timer_change_notify=NULL;

extern OSVERSIONINFO osVersion;

void start_session(SshWindow wnd, int localid, void *backhandle);
SshWindow init(Backend *back, HWND hwnd, Config cfg);
int sshterm_wndproc(SshWindow wnd, UINT message,
				WPARAM wParam, LPARAM lParam);
char *dupstr(const char *s);
int term_data(Terminal *term, int is_stderr, const char *data, int len);
void reconf(SshWindow wnd, Config *cfg);

FontSpec platform_default_fontspec(const char *name)
{
    FontSpec ret;
    if (!strcmp(name, "Font")) {
	strcpy(ret.name, "Courier New");
	ret.isbold = 0;
	ret.charset = ANSI_CHARSET;
	ret.height = 10;
    } else {
	ret.name[0] = '\0';
    }
    return ret;
}

Filename platform_default_filename(const char *name)
{
    Filename ret;
    if (!strcmp(name, "LogFileName"))
	strcpy(ret.path, "putty.log");
    else
	*ret.path = '\0';
    return ret;
}

char *platform_default_s(const char *name)
{
    if (!strcmp(name, "SerialLine"))
	return dupstr("COM1");
    return NULL;
}

int platform_default_i(const char *name, int def)
{
    return def;
}
static void gpps( const char *name, const char *def,
		 char *val, int len)
{
	strcpy(val,def);
 }

/*
 * gppfont and gppfile cannot have local defaults, since the very
 * format of a Filename or Font is platform-dependent. So the
 * platform-dependent functions MUST return some sort of value.
 */
static void gppfont(const char *name, FontSpec *result)
{
	*result = platform_default_fontspec(name);
}
static void gppfile(const char *name, Filename *result)
{
	*result = platform_default_filename(name);
}

static void gppi(char *name, int def, int *i)
{
    def = platform_default_i(name, def);
    *i = def;
}

/*
 * Read a set of name-value pairs in the format we occasionally use:
 *   NAME\tVALUE\0NAME\tVALUE\0\0 in memory
 *   NAME=VALUE,NAME=VALUE, in storage
 * `def' is in the storage format.
 */
static void gppmap(char *name, char *def, char *val, int len)
{
    char *buf = memallocn(2*len, char), *p, *q;
    gpps(name, def, buf, 2*len);
    p = buf;
    q = val;
    while (*p) {
	while (*p && *p != ',') {
	    int c = *p++;
	    if (c == '=')
		c = '\t';
	    if (c == '\\')
		c = *p++;
	    *q++ = c;
	}
	if (*p == ',')
	    p++;
	*q++ = '\0';
    }
    *q = '\0';
    free(buf);
}

BOOL init_winver(void)
{
    ZeroMemory(&osVersion, sizeof(osVersion));
    osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
    return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
}

#ifdef _MANAGED
#pragma managed(push, off)
#endif

#ifdef __cplusplus    // If used by C++ code, 
extern "C" {          // we need to export the C interface
#endif

__declspec(dllexport) BOOL term_init(Backend *back, HWND hwnd, Config cfg, SshWindow *handle) 
{
	if (back==NULL) return FALSE;
	SshWindow wnd = init(back,hwnd,cfg);
	if (wnd==NULL) return FALSE;
	*handle=wnd;
	return TRUE;

}
__declspec(dllexport) BOOL start_session(SshWindow handle, void *backhandle, int channel) 
{
	if (!handle || !backhandle || channel<0) return FALSE;
	start_session(handle, channel, backhandle);
	return TRUE;
}

__declspec(dllexport) int term_wndproc(SshWindow wnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	return sshterm_wndproc(wnd, message, wParam, lParam);
}

__declspec(dllexport) int term_data(SshWindow wnd, int is_stderr, const char *data, int len)
{
	return term_data(wnd->term, is_stderr, data,len);
}

__declspec(dllexport) void reconfig(SshWindow wnd, Config *cfg) 
{
	reconf(wnd, cfg);
}
__declspec(dllexport) void init_config(Config *cfg) 
{


	int i;

    gppi( "RFCEnviron", 0, &cfg->rfc_environ);
    gppi( "PassiveTelnet", 0, &cfg->passive_telnet);
    gppi( "BackspaceIsDelete", 1, &cfg->bksp_is_delete);
    gppi( "RXVTHomeEnd", 0, &cfg->rxvt_homeend);
    gppi( "LinuxFunctionKeys", 0, &cfg->funky_type);
    gppi( "NoApplicationKeys", 0, &cfg->no_applic_k);
    gppi( "NoApplicationCursors", 0, &cfg->no_applic_c);
    gppi( "NoMouseReporting", 0, &cfg->no_mouse_rep);
    gppi( "NoRemoteResize", 0, &cfg->no_remote_resize);
    gppi( "NoAltScreen", 0, &cfg->no_alt_screen);
    gppi( "NoRemoteWinTitle", 0, &cfg->no_remote_wintitle);
    {
	/* Backward compatibility */
	int no_remote_qtitle;
	gppi( "NoRemoteQTitle", 1, &no_remote_qtitle);
	/* We deliberately interpret the old setting of "no response" as
	 * "empty string". This changes the behaviour, but hopefully for
	 * the better; the user can always recover the old behaviour. */
	gppi( "RemoteQTitleAction",
	     no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL,
	     &cfg->remote_qtitle_action);
    }
    gppi( "NoDBackspace", 0, &cfg->no_dbackspace);
    gppi( "NoRemoteCharset", 0, &cfg->no_remote_charset);
    gppi( "ApplicationCursorKeys", 0, &cfg->app_cursor);
    gppi( "ApplicationKeypad", 0, &cfg->app_keypad);
    gppi( "NetHackKeypad", 0, &cfg->nethack_keypad);
    gppi( "AltF4", 1, &cfg->alt_f4);
    gppi( "AltSpace", 0, &cfg->alt_space);
    gppi( "AltOnly", 0, &cfg->alt_only);
    gppi( "ComposeKey", 0, &cfg->compose_key);
    gppi( "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
    gppi( "TelnetKey", 0, &cfg->telnet_keyboard);
    gppi( "TelnetRet", 1, &cfg->telnet_newline);
    gppi( "LocalEcho", AUTO, &cfg->localecho);
    gppi( "LocalEdit", AUTO, &cfg->localedit);
    gpps( "Answerback", "PuTTY", cfg->answerback,
	 sizeof(cfg->answerback));
    gppi( "AlwaysOnTop", 0, &cfg->alwaysontop);
    gppi( "FullScreenOnAltEnter", 0, &cfg->fullscreenonaltenter);
    gppi( "HideMousePtr", 0, &cfg->hide_mouseptr);
    gppi( "SunkenEdge", 0, &cfg->sunken_edge);
    gppi( "WindowBorder", 1, &cfg->window_border);
    gppi( "CurType", 0, &cfg->cursor_type);
    gppi( "BlinkCur", 0, &cfg->blink_cur);
    /* pedantic compiler tells me I can't use &cfg->beep as an int * :-) */
    gppi( "Beep", 1, &cfg->beep);
    gppi( "BeepInd", 0, &cfg->beep_ind);
    gppfile( "BellWaveFile", &cfg->bell_wavefile);
    gppi( "BellOverload", 1, &cfg->bellovl);
    gppi( "BellOverloadN", 5, &cfg->bellovl_n);
    gppi( "BellOverloadT", 2*TICKSPERSEC, &i);
    cfg->bellovl_t = i
#ifdef PUTTY_UNIX_H
		    / 1000
#endif
	;
    gppi( "BellOverloadS", 5*TICKSPERSEC, &i);
    cfg->bellovl_s = i
#ifdef PUTTY_UNIX_H
		    / 1000
#endif
	;
    gppi( "ScrollbackLines", 200, &cfg->savelines);
    gppi( "DECOriginMode", 0, &cfg->dec_om);
    gppi( "AutoWrapMode", 1, &cfg->wrap_mode);
    gppi( "LFImpliesCR", 0, &cfg->lfhascr);
    gppi( "DisableArabicShaping", 0, &cfg->arabicshaping);
    gppi( "DisableBidi", 0, &cfg->bidi);
    gppi( "WinNameAlways", 1, &cfg->win_name_always);
    gpps( "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle));
    gppfont( "Font", &cfg->font);
    gppi( "FontQuality", FQ_DEFAULT, &cfg->font_quality);
    gppi( "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode);
    gppi( "UseSystemColours", 0, &cfg->system_colour);
    gppi( "TryPalette", 0, &cfg->try_palette);
    gppi( "ANSIColour", 1, &cfg->ansi_colour);
    gppi( "Xterm256Colour", 1, &cfg->xterm_256_colour);
    gppi( "BoldAsColour", 1, &cfg->bold_colour);

    for (i = 0; i < 22; i++) {
	static const char *const defaults[] = {
	    "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0",
	    "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85",
	    "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187",
	    "85,85,255", "187,0,187", "255,85,255", "0,187,187",
	    "85,255,255", "187,187,187", "255,255,255"
	};
	char buf[20], buf2[30];
	int c0, c1, c2;
	sprintf(buf, "Colour%d", i);
	gpps( buf, defaults[i], buf2, sizeof(buf2));
	if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) {
	    cfg->colours[i][0] = c0;
	    cfg->colours[i][1] = c1;
	    cfg->colours[i][2] = c2;
	}
    }
    gppi( "RawCNP", 0, &cfg->rawcnp);
    gppi( "PasteRTF", 0, &cfg->rtf_paste);
    gppi( "MouseIsXterm", 0, &cfg->mouse_is_xterm);
    gppi( "RectSelect", 0, &cfg->rect_select);
    gppi( "MouseOverride", 1, &cfg->mouse_override);
    for (i = 0; i < 256; i += 32) {
	static const char *const defaults[] = {
	    "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
	    "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1",
	    "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2",
	    "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1",
	    "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
	    "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
	    "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2",
	    "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2"
	};
	char buf[20], buf2[256], *p;
	int j;
	sprintf(buf, "Wordness%d", i);
	gpps( buf, defaults[i / 32], buf2, sizeof(buf2));
	p = buf2;
	for (j = i; j < i + 32; j++) {
	    char *q = p;
	    while (*p && *p != ',')
		p++;
	    if (*p == ',')
		*p++ = '\0';
	    cfg->wordness[j] = atoi(q);
	}
    }
    /*
     * The empty default for LineCodePage will be converted later
     * into a plausible default for the locale.
     */
    gpps( "LineCodePage", "", cfg->line_codepage,
	 sizeof(cfg->line_codepage));
    gppi( "CJKAmbigWide", 0, &cfg->cjk_ambig_wide);
    gppi( "UTF8Override", 1, &cfg->utf8_override);
    gpps( "Printer", "", cfg->printer, sizeof(cfg->printer));
    gppi ( "CapsLockCyr", 0, &cfg->xlat_capslockcyr);
    gppi( "ScrollBar", 1, &cfg->scrollbar);
    gppi( "ScrollBarFullScreen", 0, &cfg->scrollbar_in_fullscreen);
    gppi( "ScrollOnKey", 0, &cfg->scroll_on_key);
    gppi( "ScrollOnDisp", 1, &cfg->scroll_on_disp);
    gppi( "EraseToScrollback", 1, &cfg->erase_to_scrollback);
    gppi( "LockSize", 0, &cfg->resize_action);
    gppi( "BCE", 1, &cfg->bce);
    gppi( "BlinkText", 0, &cfg->blinktext);

    gppi( "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;
    gppi( "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;
    gppi( "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;
    {
	int i;
	gppi( "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
	if (cfg->sshbug_hmac2 == AUTO) {
	    gppi( "BuggyMAC", 0, &i);
	    if (i == 1)
		cfg->sshbug_hmac2 = FORCE_ON;
	}
    }
    gppi( "BugDeriveKey2", 0, &i); cfg->sshbug_derivekey2 = 2-i;
    gppi( "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i;
    gppi( "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i;
    gppi( "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i;
    gppi( "StampUtmp", 1, &cfg->stamp_utmp);
    gppi( "LoginShell", 1, &cfg->login_shell);
    gppi( "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left);
    gppi( "ShadowBold", 0, &cfg->shadowbold);
    gppfont( "BoldFont", &cfg->boldfont);
    gppfont( "WideFont", &cfg->widefont);
    gppfont( "WideBoldFont", &cfg->wideboldfont);
    gppi( "ShadowBoldOffset", 1, &cfg->shadowboldoffset);
    gpps( "SerialLine", "", cfg->serline, sizeof(cfg->serline));
    gppi( "SerialSpeed", 9600, &cfg->serspeed);
    gppi( "SerialDataBits", 8, &cfg->serdatabits);
    gppi( "SerialStopHalfbits", 2, &cfg->serstopbits);
    gppi( "SerialParity", SER_PAR_NONE, &cfg->serparity);
    gppi( "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow);
}

#ifdef __cplusplus
}
#endif


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
			hTiming=LoadLibrary(L"timing.dll");
			if (hTiming==NULL) return FALSE;

			schedule_timer = hTiming ? (SCHEDULE_TIMER_PROC) GetProcAddress(hTiming, "schedule_timer") : NULL;
			run_timers = hTiming ? (RUN_TIMERS_PROC) GetProcAddress(hTiming, "run_timers") : NULL;
			expire_timer_context = hTiming ? (EXPIRE_TIMER_CONTEXT_PROC) GetProcAddress(hTiming, "expire_timer_context") : NULL;
			get_next_time = hTiming ? (GET_NEXT_TIME_PROC) GetProcAddress(hTiming, "get_next_time") : NULL;
			timer_change_notify = hTiming ? (TIMER_CHANGE_NOTIFY_PROC) GetProcAddress(hTiming, "timer_change_notify") : NULL;
			init_winver();
			break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
    return TRUE;
}

#ifdef _MANAGED
#pragma managed(pop)
#endif
